package com.sword.springboot.configuration;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
public class ShiroConfig {
	
	/**
	 * shiro生命周期处理器：
	 * LifecycleBeanPostProcessor，这是个DestructionAwareBeanPostProcessor的子类，
	 * 负责org.apache.shiro.util.Initializable类型bean的生命周期的，初始化和销毁。
	 * 主要是AuthorizingRealm类的子类，以及EhCacheManager类
	 * @return
	 * author sword
	 * 2017年4月19日 上午11:44:00
	 */
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
		return new LifecycleBeanPostProcessor();
	}
	
	/**
	 * 凭证匹配器：
	 * 当然在登陆认证的时候，这个类负责对密码进行编码并与数据库进行匹配。
	 * 加密方式应该与保存密码时的算法一致，应该通过配置让两边保持一致
	 * @return
	 * author sword
	 * 2017年4月19日 上午11:46:38
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher(){
		HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
		credentialsMatcher.setHashAlgorithmName("MD5");
		credentialsMatcher.setHashIterations(2);
		credentialsMatcher.setStoredCredentialsHexEncoded(true);
		return credentialsMatcher;
	}
	
	/**
	 * shiro realm域
	 * 这是个自定义的认证类，继承自AuthorizingRealm，
	 * 负责用户的认证和权限的处理，可以参考JdbcRealm的实现。
	 * @return
	 * author sword
	 * 2017年4月19日 下午12:05:16
	 */
	@Bean
	@DependsOn(value="lifecycleBeanPostProcessor")
	public ShiroRealm shiroRealm(){
		ShiroRealm shiroRealm = new ShiroRealm();
		shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return shiroRealm;
	}
	
	/**
	 * 缓存管理器:ehcache实现
	 * 缓存管理，用户登陆成功后，把用户信息和权限信息缓存起来，
	 * 然后每次用户请求时，放入用户的session中，
	 * 如果不设置这个bean，每个请求都会查询一次数据库。
	 * @return
	 * author sword
	 * 2017年4月19日 下午12:06:26
	 */
	@Bean
	@DependsOn("lifecycleBeanPostProcessor")
	public EhCacheManager cacheManager(){
		EhCacheManager cacheManager = new EhCacheManager();
		return cacheManager;
	}
	
	/**
	 * 安全管理器：shiro核心
	 * 权限管理，这个类组合了登陆，登出，权限，session的处理，是个比较重要的类。
	 * @return
	 * author sword
	 * 2017年4月19日 下午12:16:40
	 */
	@Bean
	public SecurityManager securityManager(){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(shiroRealm());
		securityManager.setCacheManager(cacheManager());
		return securityManager;
	}
	
	/**
	 * shiro过滤器工厂：
	 * 是个factorybean，为了生成ShiroFilter。
	 * 它主要保持了三项数据，securityManager，filters，filterChainDefinitionManager。
	 * @return
	 * author sword
	 * 2017年4月19日 下午1:46:13
	 */
	@Bean(value="shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager());
		
		Map<String, Filter> filters = new LinkedHashMap<>();
		LogoutFilter logoutFilter = new LogoutFilter();
		logoutFilter.setRedirectUrl("/login.jsp");
		filters.put("logout", logoutFilter);
		shiroFilterFactoryBean.setFilters(filters);
		
		shiroFilterFactoryBean.setLoginUrl("/login.jsp");
		
		Map<String, String> filterChainDefinitionManager = new LinkedHashMap<>();
		//filterChainDefinitionManager.put("/logout", "logout");
		//filterChainDefinitionManager.put("/user/**", "authc,roles[user]");
		//filterChainDefinitionManager.put("/shop/**", "authc,roles[shop]");
		//filterChainDefinitionManager.put("/admin/**", "authc,roles[admin]");
		filterChainDefinitionManager.put("/manage/**", "authc,roles[admin]");
		filterChainDefinitionManager.put("/login.do", "anon");
		filterChainDefinitionManager.put("/**", "authc");
		//filterChainDefinitionManager.put("/**", "anon");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);
		
		//shiroFilterFactoryBean.setLoginUrl("/login.do");
		//shiroFilterFactoryBean.setSuccessUrl("/");
		//shiroFilterFactoryBean.setUnauthorizedUrl("/403");自定义错误页面
		
		return shiroFilterFactoryBean;
	}
	
	/**
	 * Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。
	 * @return
	 * author sword
	 * 2017年4月19日 下午2:09:22
	 */
	@Bean
	@ConditionalOnMissingBean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
		DefaultAdvisorAutoProxyCreator daapc = new DefaultAdvisorAutoProxyCreator();
		daapc.setProxyTargetClass(true);
		return daapc;
	}
	
	/**
	 * shiro里实现的Advisor类，内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
	 * 老实说，这里注入securityManager，我不知道有啥用，从source上看不出它在什么地方会被调用。
	 * @return
	 * author sword
	 * 2017年4月19日 下午2:10:20
	 */
	@Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager());
        return aasa;
    }
	
	/*thymeleaf里面的
	@Bean(name = "shiroDialect")
	public ShiroDialect shiroDialect(){
		return new ShiroDialect();
	}
	*/
}
